package org.example.api;

import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class WeatherClient {

    private Map<String, String> cache = new ConcurrentHashMap<>();
    private String apiUrl;
    private int retryCount;        //retry count
    private int requestTimeout;        //request timeout
    private long cacheExpireTime;
    private volatile boolean cacheInit;
    private volatile long lastWriteCacheTime;

    private WeatherClient next;

    public WeatherClient(String apiUrl, int retryCount, int requestTimeout, int cacheExpireTime) {
        this.apiUrl = apiUrl;
        this.retryCount = retryCount < 0 ? 0 : retryCount;
        this.requestTimeout = requestTimeout < 0 ? 0 : requestTimeout;
        this.cacheExpireTime = TimeUnit.SECONDS.toMillis(cacheExpireTime < 0 ? 0 : cacheExpireTime);
    }

    public Optional<String> findCodeByName(String code, String name){
        refreshCache(code);
        return Optional.ofNullable(cache.get(name));
    }

    private void refreshCache(String code){
        if(!cacheInit || lastWriteCacheTime + cacheExpireTime < System.currentTimeMillis()){
            synchronized (this) {
                if(!cacheInit || lastWriteCacheTime + cacheExpireTime < System.currentTimeMillis()){
                    JSONObject jsonObject = getJSON(code);
                    jsonObject.forEach((k, v)->{
                        cache.put(v.toString(), k);
                    });
                    lastWriteCacheTime = System.currentTimeMillis();
                    cacheInit = true;
                }
            }
        }
    }

    public JSONObject getJSON(String code){
        code = Optional.ofNullable(code).orElse("");
        String url = apiUrl.replaceAll("\\{code\\}", code);
        Optional<String> resultCode = request(url);
        if(!resultCode.isPresent()){
            throw new JSONException(url + " response is not json");
        }
        String result = resultCode.get();
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(result);
        } catch (Exception e) {
            throw new JSONException(url + " response is not json");
        }
        return jsonObject;
    }

    public Optional<String> request(String url){
        //retry mechanism
        int retryCount = 0;
        while(retryCount++ < this.retryCount){
            HttpResponse execute = null;
            try {
                execute = HttpRequest.get(url)
                        .timeout(requestTimeout)//超时，毫秒
                        .disableCookie()
                        .execute();
            } catch (HttpException e) {
                if (retryCount == this.retryCount) {
                    throw new RuntimeException(url + " request time out:" + e.getMessage());
                }
                continue;
            } catch (Exception e) {
                throw new RuntimeException(url + " error:" + e.getMessage());
            }
            int status = execute.getStatus();
            if(execute.isOk()){
                return Optional.of(execute.body());
            }else if(status == 302 || status == 404 || status >= 500){
                throw new JSONException(url + " response is not json");
            }
        }
        return Optional.empty();
    }
}
